| | The Object Engine(tm) | C++ Class Library | and Code Generator | for The Paradox Engine(tm) | | Version 2.0 | October 2, 1993 | | Dr. Mark Brittingham | | | The Object Engine is a C++ class library that encapsulates the core functionality of the Paradox Engine database library. This encapsulation provides a significantly easier interface to the Paradox Engine functions. Rather than spending your time writing functions to copy data back and forth between data structures and record buffers, you need only call "Store", and "Link" methods of the object engine's persistant classes. Furthermore, these functions (and others) are written for you automatically. You simply specify the fields that will populate your new class, and the Code Generator automatically generates the class and all of its methods for you! The generated code will work under both Windows and DOS and can be compiled with Borland C++ 3.1 (a sample is included to help guide you) or MSVC++. The MSVC version is available only if you register the software and request it. The library covers Alphanumeric, Short, Long, Double, Float (currency), Date, and Blob types. The generator allows you to create both primary and secondary indices. And, you can create your class instances using the primary or any secondary index to open the underlying table (including composite indices). The library is very efficient: it adds less than 19K to your application (with speed optimization). Finally, the Object Engine's BLOB support includes two functions that simplify BLOB programming. Blob::OEPut and Blob::OEGet put and get BLOB data with one call in each direction. You simply pass in or read out the data in character arrays. When reading the data, we even create the data array for you. To use these functions, your BLOB data must be no larger than 64K bytes to use these functions. Of course, an example is included in this package (see blobmain.cpp). Disclaimer ========== The Object Engine is now SHAREWARE. If you plan on using the software, you must register the software and pay a (modest) registration fee. You will receive the source code to the objeng(w).obj files (which will allow you to set defaults for the number of tables, size of cache, etc.) when you register. See the register.txt file for information. Brittingham Software Design, Inc. disclaims all warranties as to this software, whether express or implied, including without limitation any implied warranties of merchantability, fitness for a particular purpose, functionality, data integrity or protection. Brittingham Software Design, the Brittingham Software Design logo, Object Engine(tm), and ObjEng(tm) are trademarks of Brittingham Software Design, Inc. Paradox(tm) and the Paradox Engine(tm) are trademarks of Borland International, Inc. MSVC(tm) and Microsoft Visual C(tm) are trademarks of Microsoft, Inc. MS-DOS(tm), and Windows(tm) are trademarks of Microsoft Corporation. Netware and Novell are registered trademarks of Novell, Inc. Trademarks of other companies mentioned in this documentation appear for identification purposes only and are the property of their respective companies. Using the Library ----------------- To use the library, you should first plan out your database structure. You should have a clear idea of the classes needed in your application and their interrelationships. Next, run the Gen2.exe application (it runs under Microsoft Windows(tm)) and create each of your classes. Gen2 is quite simple. A single button allows you to create a class or to edit whichever class is highlighted in a list of classes. You can save your class definitions in a format recognized by Gen2 and restore them later. Thus, you need not complete the entire set of class definitions in one sitting. When you are satisfied with the set of classes you have defined, you can generate the code to implement these classes as persistant objects with the push of a button. You will specify the directory and filename of the header and C++ files. Keep in mind that you can go back, change your classes, and regenerate your code. However, any changes YOU make to the generated code will be overwritten (unless you use another file name). Thus, it is best to make changes in a separate file. If lots of people register, I will consider adding a parsing facility to the Object Engine. No further documentation is included for the generator simply because it is so straightforward. Please fire it up and see for yourself how quickly you can generate some real persistant classes. To work with the generated code, you need to do three things: 1) include the generated header file in the appropriate code files, 2) place the generated C++ file in your project or makefile, and 3) place the objeng.obj file in your project/makefile (note: use objengw.obj if working in Windows). Remember what you named the header and C++ files during the generate phase so you know what to include in steps 1 and 2. The objeng(w).obj file is included in the Object Engine archive file. Now you are ready to declare, manipulate, store, or retrieve instances of the classes you have defined! You never need to worry about initializing the engine, manipulating or creating tables, or reading or writing data. Of course, you do have control over Paradox Engine initialization (if you register the software) as well as table manipulation if you need it. But, for the vast majority of your work, you need only deal with standard C++ syntax. Two Keys To Object Engine Programs ---------------------------------- Two core ideas will help you to understand how to use the library. First, to store an object in the library, simply call its "Store" method. To retrieve a database record and place it in an object, you will "link" the object to the record. This can be done either by the contents of a field (possibly a key field), or navigationally (first, next, etc.). Thus "linking" is the act of making an in-memory data structure reflect an on-disk record. Of course, you needn't think of it in terms of database records at all! The following link functions are defined: LinkToKey (one or more key fields) LinkToSKey (to link to secondary & composite keys) LinkToRecord (a record position) LinkToField (contents of a field) LinkToID (objectID - see below) LinkToFirst LinkToLast LinkToNext LinkToPrev The second key idea is found in the "objectID". Each object has an objectID associated with it. When you link an object to a particular database record, your object is given the record's unique objectID. This allows each instance of a persistant class to have its own pointer into the database and allows you to store objects without worrying about whether they are new or an update to a previously stored record. However, you need to keep these object IDs in mind when working with persistant classes. For example, if you create a new object using the copy constructor or set one object to another (using '='), then both will have the same objectID. If you change these two objects separately and save them both, then the record will reflect the last one stored. That is, storage is mediated by the objectID. An object without an objectID will be stored as new (assuming that there are no conflicting key fields). An object whose objectID matches a record found in the database will update that record. If you simply keep in mind that an object has an identity that you must respect, you will have no problem using the library. Note that the use of objectIDs has one other implication; if you are using objeng data tables with other tools (i.e. Paradox), you will have to maintain the objectIDs. To do this, you have to use the "objeng.db" table. This table has one record and one field (a long value ("N") named "ID"). To manage records outside of the objeng, you will have to retrieve this record and use the value in its ID field whenever you create a new objeng object. Remember that every record in every table has its own unique objectID. After retrieving the record, you MUST increment the value and place it back in the table. A table is used so all applications can have access to the objectID index, possibly with locking and concurrence. *************************************************************************** Persistant Class Functions - Introduction When you generate code using gen2.exe, you will see that your class definition: a) is derived from "PersistClass", b) has one or more variable declarations, and e) has only six other methods (2 Constructors, operator=, Store, Retrieve, and LinkToKey). In addition to the six functions defined with the class, your class inherits many functions for linking to various records as well as network (locking) operations, informational functions, Paradox Engine interface methods (getting table and record handles, etc.), and more. All of these functions are documented below. Functions that return a value will return Paradox Engine error codes (PXSUCCESS, PXERR_XXX). In all cases (functions, operators, constructors), the variable "status" will be filled with the most recent Paradox operation's return value (use GetStatus() to inspect). Thus, you will need to be aware of the Paradox Engine's error codes. If you created a secondary key in the Gen2 code generator, you may call object constructors with the secondary key ID as a parameter. If you do so, the link-to functions will retrieve records sorted by the secondary key. *************************************************************************** Class Function Documentation For purposes of clarity, assume that you have declared a class "Client." The following functions would be defined: //------------------- Native (Generated) Functions ------------------------ //------------------------------------------------------------------------- Constructor: Client(int indexID = 0) [format: (int indexID)] Creates an instance of Client. If the client database does not exist, it is created. If it is not open, it is automatically opened. The object does not reflect any record in the database when first created. All strings are empty, all scalar values are set to zero. The database is opened on the index given by indexID. If indexID is omitted, it defaults to 0 (opened on primary index or by physical order). Otherwise, the secondary index given by indexID is used (the table will appear to be sorted in secondary index order). To open a table on a composite secondary key, you will have to open a table with no index. Then call "GetFieldHandle" with the name of the composite key to get its numerical field handle (the name is all you know ahead of time). "GetFieldHandle" is a method of PersistClass and is documented below. Since key handles stay the same (except, possibly, if you drop and then re-add them), you could do this in one sample run and then just use the returned field handle (as a constant) for all subsequent runs. //------------------------------------------------------------------------- Constructor: Client(Client& val) [the copy constructor] Creates a new, identical instance of Client. The client database will be created and open since you must have already created a client to pass in! The new object reflects all of the values of the passed in object. This includes the objectID of the object. Thus, you will now have two objects looking at exactly the same database record. Moving either object (using LinkTo...) will not affect the other. //------------------------------------------------------------------------- Operator: Client &operator=(Client &) [the assignment operator] See the copy constructor. Best used during the declaration of a reference. //------------------------------------------------------------------------- Private function: int Retrieve [note "Private", do not make public!] Copies from the database record to the class' variables. Called from all of the "Link..." functions (see below). Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int Store() [see PXRecInsert, PXRecUpdate] Moves the contents of the object into the database. If the object is already linked to a database record (objectID != 0) then this function will UPDATE the matching database record. If the object is not yet linked to a database record (objectID is equal to 0), then calling Store will add it to the database (PXRecInsert). It is an error to store a record with a duplicate primary key. If you are working with a non-keyed table, a new record will be stored in the position before the current record. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LinkToKey(int numkeys, int mode) [See also, PXSrchKey] When you define a class, you can specify one or more of its variables (starting with the first), as keys. A key variable corresponds to key fields in the database. To search using key values, you place these values in the object's key variables and call this function. For example, if you have three key fields and want to search on the first two, you should place values for these first two key fields in the corresponding variables and call this function with numkeys = 2. Set Mode equal to any mode legal under the PXSrchKey documentation (SEARCHFIRST, etc.) according to your search requirements. For example: ... Client.ID = 1000; if (Client.LinkToKey(1, SEARCHFIRST) == PXSUCCESS) Note: if the field is not keyed, this function does nothing but return an error. New in V2.0: If you perform a CLOSESTRECORD match, then the object engine will retrieve the record even if a PXERR_RECNOTFOUND error was returned (thanks for the suggestion: Mike Sagner). Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LinkToSKey(char *fldName, int mode) [See also, PXSrchFld] If you define one or more secondary keys in the Gen2 program, secondary keys will be automatically created when your table is created (by default, these are INCSECONDARY keys: see Paradox Engine documentation). You can use these keys by filling the appropriate fields with your search criteria and calling the LinkToSKey function. This function takes the name of the field (or composite key) and the mode (SEARCHFIRST, SEARCHNEXT, etc.). Note that the case-insensitive and composite fields you create are accessed by name (by default, Gen2 creates case-insensitive names as "CI". If you forget the name of a composite or case-insensitive key, you can look in your class constructor. For each such key there will be a "RegisterCompKey" call; the key name is the fourth argument to this call. The MAIN.CPP example file demonstrates a call to LinkToSkey. New in V2.0: If you perform a CLOSESTRECORD match, then the object engine will retrieve the record even if PXERR_RECNOTFOUND was returned (thanks for the suggestion: Mike Sagner). //------------------- Inherited Functions --------------------------------- //------------------------------------------------------------------------- Function: int LinkToField(char *fieldname, void *value, int mode) [See PXSrchFld] To search for a specific value in a specific field, pass in the fieldname, the value (cast to a void pointer), and the search mode. For example: client.LinkToField("LNAME", (void *)"Brittingham", SEARCHFIRST) client.LinkToField("ID", (void *)&i, SEARCHFIRST) Note that the value must be a void pointer. If you really want overloaded functions, let me know. It takes up more space but might be worth doing. Note also that searches using compound keys are on the way... New in V2.0: If you perform a CLOSESTRECORD match, then the object engine will retrieve the record even if a PXERR_RECNOTFOUND error was returned (thanks for the suggestion: Mike Sagner). Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LinkToRecord(int position) [See PXRecGoto] Make object reflect contents of the nth record where n = position. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LinkToID(const long &ID) [See PXSrchFld] Explicitly set object to database record with objectID = ID. Should not generally be needed. Note, no mode argument since objectIDs are unique. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LinkToFirst() Make object reflect contents of first database record. If database is empty, contents are not changed and error code is returned. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LinkToLast() Make object reflect contents of last database record. If database is empty, contents are not changed and error code is returned. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LinkToNext() Make object reflect contents of next database record. If there is no next record, contents are not changed and error code is returned. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LinkToPrev() Make object reflect contents of previous database record. If there is no previous record, contents are not changed and error code is returned. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: void Unlink(); Removes the objectID from the object. This allows you to copy an object and then unlink it from the database record. If you then Store the object it will go in under a new objectID (assuming there is no key conflict). Return Value: None //------------------------------------------------------------------------- virtual int Destroy() [See PXRecDelete] Deletes the database record to which this object corresponds. If the object has not been linked, it returns PXERR_RECNOTFOUND. Does not delete the object or change any of its values. Note that deleting an object does not remove it from the database. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int GetFieldHandle(char *fldName, FIELDHANDLE &fieldHandle) Retrieves the handle of the field named by fldName and places it in fieldHandle. Useful for figuring out what handle has been assigned to a composite key. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- int NRecs(RECORDNUMBER & nrecs) [See PXTblNRecs] Retrieves the number of records in the table that the object works with. The number of records is placed in the nrecs parameter. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- const int GetStatus() Returns the status value. Status is set by the most recent Paradox Engine function. This function is useful for testing the success of functions that don't return a status value (constructors and operators) although it can be used after any Object Engine call. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- TABLEHANDLE GetTblHdl() Returns the Paradox Engine table handle. Can be used if you want to bypass the library and work directly with the table. Since the Object Engine does not provide direct table manipulation (copy, delete, etc.), you may find this function useful. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- RECORDHANDLE GetRecHdl() Returns the Paradox Engine record handle. Can be used to bypass the library and work directly with the record buffer. Should not be needed unless you want to work with multi-field secondary indices (which will be available in the next release anyhow). Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- const char *GetTblName() Returns the name of the table to which this object is attached. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- long GetObjectID() Returns the objectID. Should not be needed. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- int OEPutDate(const int &fieldno, const char *date) and int OEPutDate(const int &fieldno, const struct tm &date). int OEGetDate(const int &, char *date) int OEGetDate(const int &, struct tm &date) Used in the classes derived from PersistClass to place dates into specific record buffer fields. Used to simplify the process of generating code. You should never need to use these functions since you will not work directly with record buffers. The code generator will create either char dates ("11/27/65") or struct tm dates depending on the "Date Type" switch (toggled in the main menu). NOTE! The implementation of struct tm structures in Borland C++ has an odd feature: zero-based months. You will get these if you use the date functions to automatically fill a struct tm (i.e. Today's date). Before storing struct tm structures in the database, I add 1 to the month and subtract 1 from the month when retrieving. IF YOU ARE NOT USING 0-BASED MONTHS, YOU SHOULD CHANGE THIS ASPECT OF OEGET/PUTDATE (you will need the objeng.cpp source code). Return Value: PXSUCCESS or other engine error code. //------------------- Network/Locking Functions --------------------------- //------------------------------------------------------------------------- These functions implement the network management for the Object Engine. Note that you must initialize the Object Engine (ObjEng object) with a user name, net name path, and netcode for these functions to be effective. In particular, the netcode argument passed to the ObjEng constructor should be NETSHARE or LOCALSHARE. Note that no harm is done if these functions are in your code when running under NOSHARE (or NOTONNET); they just won't do anything. There is no encapsulation of the PXNetErrUser and PXNetUserName functions since there is no object on which these functions bear (and thus no value to encapsulating them). You will, however, find these functions useful in network programming. See pp. 116-139 of the Paradox Engine "C Reference" for more information. //------------------------------------------------------------------------- Function: int LockFile(int lockType); [See PXNetFileLock ] See Paradox Engine for lockType documentation. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int UnlockFile(int lockType); [See PXNetFileUnlock ] Unlocks the object's Table file. You may want to use UnlockTable instead. See Paradox Engine for lockType documentation. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LockRecord(LOCKHANDLE &lckHandle); [See PXNetRecLock ] Makes sure that the current record of the underlying table points to this object (updating or inserting a record as necessary) and then locks it. If the lock succeeds, the lckHandle parameter holds the handle for this record. You can return to this record, knowing that it hasn't been touched by another user, using the GotoLock function. The IsRecordLocked function (next) can be used to query an object whether the current record has a lckHandle on it. It is an error to lock one record more than once. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int IsRecordLocked(int &isLocked); [See PXNetRecLocked ] Makes sure that the current record of the underlying table points to this object (does not update or insert record) and then queries its lock status. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int UnlockRecord(int lckHandle); [See PXNetRecUnlock ] Unlocks the record locked with lckHandle. Does not change the record pointed to or the object itself. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int GotoLock(LOCKHANDLE lckHandle); [See PXNetRecGotoLock ] Moves to the locked record and fills the object with the data in this record. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LockTable(int lockType); [See PXNetTblLock ] Locks the underlying table with the given lockType. For example, if the "Client" object uses the "client" table, then that table is locked. See Paradox Engine for lockType documentation. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int UnlockTable(int lockType); [See PXNetTblUnlock ] Unlocks the lock placed on the underlying table by the LockTable function described above. See Paradox Engine for lockType documentation. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int IsTblChanged(int &isChanged); [See PXNetTblChanged ] Query function. Stores TRUE or FALSE in the isChanged parameter. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int TableRefresh(); [See PXNetTblRefresh ] This function resynchronizes the internal PXEngine cache with the database in case any changes have been made. Should test with IsTblChanged before calling this function. Return Value: PXSUCCESS or other engine error code. *************************************************************************** The Blob Class - Introduction The Blob class allows you to store large text or binary objects in the Object Engine database. Most of the Object Engine Blob methods provide a thin encapsulation around the corresponding Paradox Engine function calls. They do simplify blob programming, however, since they take care of the record and field handles for you. The real value added lies in the OEGet and OEPut functions. Not only do these functions provide one-call storage and retrieval of blob data, they also illustrate blob programming techniques that you will use when using the more basic functions. You will need the more primitive functions only if you need to store blobs whose size exceeds 64K bytes. You will use Blob objects by declaring a field to be of type 'Mxxx', 'Bxxx', 'Fxxx', 'Oxxx', or 'Gxxx' in the code generator (where xxx is some positive integer). When you generate code, one or more of the fields in your class will be instances of class "Blob". To work with the blob data, you will use the blob pointers attached to this class instance. For example, if the field "imageblob" is a blob field, the following would store data in the blob and write the blob to the "public" area of the blob file (see the PXEngine documentation for a discussion of Private and Public blobs): // Put the buffer into the blob using the ObjEngine shortcut... dbclass.imageblob.OEPut(imagebuffer, BUFLEN); // NOTE: Must STORE the class before its blob is publicly stored! dbclass.Store(); An illustration of Blob programming using the OEGet and OEPut functions can be found in the blobmain.cpp file in this package. Of course, all of the PXEngine blob functions are covered as well. These functions follow the same naming convention as the Paradox Engine except that "PXBlob" is stripped from the front of the function name. Since these are methods of the Blob class, the prefix is redundant. Their arguments are also the same as the Paradox Engine and in the same order EXCEPT the RECORDHANDLE and FIELDHANDLE arguments are always omitted. Record and Field are implied by the Blob object you are using (the Persistant Object you are working with holds the record handle, the Blob instance within the persistant object gives the field handle). These functions are documented below. *************************************************************************** Blob Class Function Documentation All of the functions in this category that return a value (except operator=) return the last Engine state (PXSUCCESS, PXERR_XXX). //------------------------------------------------------------------------- Constructor: void Blob(OEHandle &, FIELDHANDLE) You should never have a reason to use this constructor. Leave it alone- its bad for your health. This function is used in the persistant class constructor once we know some facts about the table. The FIELDHANDLE given to this function corresponds to the field given in Gen2. The OEHandle argument is responsible for letting a Blob know which table record it works with. //------------------------------------------------------------------------- Function: Blob &operator=(const Blob &inblobobj) Used in the Persistant Object operator= so we can just set one blob equal to another without worrying about internal variables. //------------------------------------------------------------------------- Function: int Clone() See PXBlobClone: creates a private blob so no one else screws you up by changing a blob while you are in the middle of reading one. //------------------------------------------------------------------------- Function: int Close(int accept) See PXBlobClose: Closes blob opened by OpenRead or OpenWrite. Accept determines the final disposition of blob (see PXEngine doc!). //------------------------------------------------------------------------- Function: int Drop() See PXBlobDrop: Clears space taken by a blob. //------------------------------------------------------------------------- Function: int OpenRead() See PXBlobOpenRead: Prepares a blob for reading (opens a blob handle). Don't worry about the blob handle when you open a blob! I automatically keep it around for your next Get or GetSize operation so you don't even have to know that one exists. //------------------------------------------------------------------------- Function: int OpenWrite(unsigned long size, int saveCurrent) See PXBlobOpenWrite: Prepares a blob for writing (opens a blob handle). Again, don't worry about the blob handle, that's why you have me around... See PXEngine documentation for saveCurrent information. //------------------------------------------------------------------------- Function: int GetSize(unsigned long *size) See PXBlobGetSize: Returns the size of a blob you have opened (usually for reading). Generally, this function is what tells you how many times you have to call Get. //------------------------------------------------------------------------- Function: int Get(unsigned size, unsigned long offset, void* buf) See PXBlobGet: Puts blob data into buf. All of the handles (record, field, and blob) are taken care of for you. Called in chunks to get all of the data (limit 64K at a time). If you know you will only need one Get, then use OEGet and skip all the rest of this stuff. //------------------------------------------------------------------------- Function: int QuickGet(int bufsize, void* dest, int* bytesRead) See PXBlobQuickGet: Grabs whatever header data you've stored directly in the table (up to 240 bytes). //------------------------------------------------------------------------- Function: int Put(unsigned size, unsigned long offset, void* buf) See PXBlobPut: Puts buf into private blob. All of the handles (record, field, and blob) are taken care of for you. Called in chunks to put all of the data (limit 64K at a time). If you know you will only need one Put, then use OEPut and skip all the rest of this stuff. //------------------------------------------------------------------------- Function: int OEPut(char *buff, unsigned size) See example above and source code. Just stick "buff" in a blob. Doesn't get written to the "public" area (sort of like "written to the table") until you do a "Store" on the persistant object to which the blob belongs. //------------------------------------------------------------------------- Function: int OEGet(BlobData &bdata) Hey, this Blob stuff is easy after all, isn't it? Grab a blob using this function. The BlobData structure is a struct: typedef struct { char *data; <- Holds the blob data unsigned size; <- Holds the size of the blob } BlobData; This is used so that you will have your data and know how big it is too... Note that when you create an instance of BlobData before calling OEGet, the "data" field is empty (points to nothing). When you call OEGet, I create a buffer of the appropriate size, make "data" point to it, and place the buffer size in "size". Finally, I read the blob data into the "data" field of the BlobData struct and return. YOU have to remember to delete the memory that "data" points to. After all, I can't do everything for you. For an example of OEGet see the "blobmain.cpp" file. //------------------------------------------------------------------------- Function: int GetStatus() const Returns the status value. This value is always set to the return code from the last PXEngine function call (which is also what most functions return). Especially useful for testing the result of a constructor call! //------------------------------------------------------------------------- Function: int GetRHdl() const and int GetFHdl() const For internal use. You can use these to tell you what the blob thinks its handles are. *************************************************************************** Object Engine Methods of Interest The object engine class has a number of methods that may be of interest in your database programming. First, the constructors for this class are responsible for setting the Paradox Engine defaults and initializing the engine. You can change the arguments to this declaration to tell the object engine where your data base files are or to give it information about your network status. Note that you can NOT change the cache size or number of table handles using the objeng constructor parameters anymore. Instead,the parameters should be set up with capacities appropriate for your application (once) by modifying the ObjEng constructor in the objeng.cpp file. Of course, you will need the source code to do this...and you will need to register to get the source code... When Gen2 generates code for you, a global "objeng" pointer variable is placed at the top of the generated file. It is up to you to include an "extern" pointer to objeng in your "main" cpp file and to initialize objeng in your winmain() or main() function. By default, the Object Engine supports 4 tables and a 16K cache. //------------------------------------------------------------------------- ObjEng::ObjEng(char *tableDirectory, char *userName, char *netNamePath, int netCode) or There are no longer separate constructors for Windows and DOS. The same constructors are used in both cases. The first constructor is used if you wish to declare a table directory (where database files are to be stored). Under Windows or in a DOS network environment you should declare a userName (generally the user's name or the name of the application). This is the name the application instance will be known by when locks are placed. The netNamePath parameter specifies where the network lock files are located (use the directory only: "q:\\paradox\\"). The netCode parameter specifies the kind of network connection (see page 122 of the "C Reference" manual). Important WINDOWS 3.1 Note: The ObjEng constructor now takes care of changing the WIN.INI file automatically if the application's resource needs exceed the settings in WIN.INI. Thus you do NOT have to distribute and document the PXENGCFG.EXE file. I always thought it was a little stupid to have the application blow up if the configuration was shy of a few table handles rather than just change the configuration. So...I fixed it! See the objeng.cpp file for details. //------------------------------------------------------------------------- ObjEng::ObjEng() This constructor uses defaults for all of the needed parameters (directory is the same as the application, name is "Objeng", and no network is declared). //------------------------------------------------------------------------- Functions: int ObjEng::DropKey(char *tblname, FIELDHANDLE fldID) int ObjEng::RegisterKey(char *tblname, int nIdxFields, FIELDHANDLE *IdxFieldHandles, int idxType) int ObjEng::RegisterCompKey(char *tblname, int nIdxFields, FIELDHANDLE *IdxFieldHandles, char *keyName, int idxType, int caseMode) These functions add or drop keys from the named tables. You cannot use the RegisterXXX functions while the corresponding tables are open. They can safely be used in the constructor in the IsRegistered block between "EnsureExist" and "Register". See the generated constructors for examples of how to use these functions. The "idxType" arg is PRIMARY, SECONDARY or INCSECONDARY (PRIMARY not allowed in RegisterCompKey). The "caseMode" argument is either CASESENSITIVE or CASEINSENSITIVE. The DropKey function is a wrapper for the Engine's PXKeyDrop function. //------------------------------------------------------------------------- ** Please avoid using any other Object Engine class methods. These are for internal book-keeping only and their inappropriate use may result in corruption of your databases. Copyright (c) 1993, Brittingham Software Design, Inc.